package com.blb.common.util;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * RabbitMQ的配置
 */
@Slf4j
@Configuration
public class RabbitMQConfig {

    public static final String QUEUE_COURSE_SAVE = "queue.course.save";
    public static final String QUEUE_COURSE_REMOVE = "queue.course.remove";
    public static final String KEY_COURSE_SAVE = "key.course.save";
    public static final String KEY_COURSE_REMOVE = "key.course.remove";
    public static final String COURSE_EXCHANGE = "edu.course.exchange";

    /**
     * 保存课程的队列
     * @return
     */
    @Bean
    public Queue queueCourseSave() {
        return new Queue(QUEUE_COURSE_SAVE);
    }

    /**
     * 删除课程的队列
     * @return
     */
    @Bean
    public Queue queueCourseRemove() {
        return new Queue(QUEUE_COURSE_REMOVE);
    }

    /**
     * 主题模式的交换机
     * @return
     */
    @Bean
    public TopicExchange topicExchange() {
        return new TopicExchange(COURSE_EXCHANGE);
    }

    /**
     * 绑定保存课程队列和交换机
     * @return
     */
    @Bean
    public Binding bindCourseSave() {
        return BindingBuilder.bind(queueCourseSave()).to(topicExchange()).with(KEY_COURSE_SAVE);
    }

    /**
     * 绑定删除课程队列和交换机
     * @return
     */
    @Bean
    public Binding bindCourseRemove() {
        return BindingBuilder.bind(queueCourseRemove()).to(topicExchange()).with(KEY_COURSE_REMOVE);
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        //设置发布确认机制的回调方法 correlationData消息数据，ack是否应答，cause失败原因
        rabbitTemplate.setConfirmCallback((correlationData,ack,cause) -> {
            if(ack){
                log.info("交换机收到了生产者消息：{}",correlationData);
            }else{
                //出现发送失败，将消息落库，定时扫描失败消息，重新发送
                log.error("消息发送交换机出现失败将消息落库，定时扫描失败消息重新发送：{}",correlationData);
            }
        });
        //设置消息发送失败返回回调，参数：消息内容，响应代码，响应文本，交换机，路由键
        rabbitTemplate.setReturnCallback(((message, replyCode, replyText, exchange, routingKey) -> {
            //消息补偿的逻辑，如：保存失败消息，通过定时任务重新投递
            log.error("消息发送失败，message:{}, replyCode:{}, replyText:{}, exchange:{}, routingKey:{}",message, replyCode, replyText, exchange, routingKey);
        }));
        return rabbitTemplate;
    }
}
